package com.bclz.nio.project;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Iterator;
import java.util.Set;

public class GroupChatSystem {

    private DateTimeFormatter formatter=DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private ServerSocketChannel serverSocketChannel;
    private Selector selector;

    public GroupChatSystem(){

        try {
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress("127.0.0.1",6667));
            serverSocketChannel.configureBlocking(false);
            selector = Selector.open();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void listen(){
        try {
            //serverSocketChannel注册selector,已经准备好接受channel连接
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while(true){
                int select = selector.select(1000);
                if(select <1){
                    //1秒钟内无连接
//                    System.out.println(" no client connect in 1s..");
                    continue;
                }
                //有事件发生，获取对应的key,通过key反向获取对应的channels
                Set<SelectionKey> selectionKeys = selector.selectedKeys();
                Iterator<SelectionKey> selectionKeyIterator = selectionKeys.iterator();
                while(selectionKeyIterator.hasNext()){
                    SelectionKey selectionKey = selectionKeyIterator.next();
                    if(selectionKey.isAcceptable()){
                        //客户端连接,服务端接受连接...
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        InetSocketAddress remoteAddress =(InetSocketAddress) socketChannel.getRemoteAddress();
                        System.out.println(String.format("[%s] %s:%s已连接...", LocalDateTime.now().format(formatter),remoteAddress.getHostString(),remoteAddress.getPort()));
                        socketChannel.configureBlocking(false);
                        //注册通道
                        socketChannel.register(selector,SelectionKey.OP_READ,ByteBuffer.allocate(1024));
                    }else if(selectionKey.isReadable()){
                        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                        InetSocketAddress remoteAddress = (InetSocketAddress) socketChannel.getRemoteAddress();
                        try{
                            ByteBuffer byteBuffer=(ByteBuffer)selectionKey.attachment();
                            StringBuilder sb=new StringBuilder();
                            while(socketChannel.read(byteBuffer)>0){
                                sb.append(new String(byteBuffer.array(),"UTF-8"));
                                /**通道数据需要读取干净,必须得clear，
                                 * nio是一种水平触发(操作系统认为数据没读完，会一直通知),
                                 * 下次再有数据进来，read到该ByteBuffer缓冲区,如果缓冲区够大(position位置会一直移动，会有重复数据)，
                                 * 如果缓冲区比较小(比如刚好只够一次),数据read就会=0或者小于1相当于select会一直出发读事件
                                 * */
                                byteBuffer.clear();
                            }
                            System.out.println(String.format("[%s] %s:%s message：%s", LocalDateTime.now().format(formatter), remoteAddress.getHostString(), remoteAddress.getPort(),sb.toString()));
                            dispatchMessage(socketChannel,sb.toString());
                        }catch (Exception e){
                            selectionKey.cancel();
                            socketChannel.close();
                            System.out.println(String.format("[%s] %s:%s已下线...", LocalDateTime.now().format(formatter), remoteAddress.getHostString(), remoteAddress.getPort()));
                        }

                    }
                    selectionKeyIterator.remove();
                }

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void dispatchMessage(SocketChannel socketChannel, String msg) {
        //获取所有通道
        Set<SelectionKey> selectionKeys = selector.keys();
        selectionKeys.forEach(k->{
            SelectableChannel channel = k.channel();
            if((channel instanceof SocketChannel) && channel!=socketChannel){
                SocketChannel otherSocketChannel = (SocketChannel) channel;
                try {
                    System.out.println("发送给:"+((InetSocketAddress)otherSocketChannel.getRemoteAddress()).getPort());
                    otherSocketChannel.write(ByteBuffer.wrap(msg.getBytes("UTF-8")));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        });

    }


    public static void main(String[] args) {

        GroupChatSystem groupChatSystem=new GroupChatSystem();
        groupChatSystem.listen();

    }

}
